package com.atguigu.daijia.order.service.impl;

import com.atguigu.daijia.common.config.redis.RedissonConfig;
import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.model.entity.order.*;
import com.atguigu.daijia.model.enums.OrderStatus;
import com.atguigu.daijia.model.form.order.OrderInfoForm;
import com.atguigu.daijia.model.form.order.StartDriveForm;
import com.atguigu.daijia.model.form.order.UpdateOrderBillForm;
import com.atguigu.daijia.model.form.order.UpdateOrderCartForm;
import com.atguigu.daijia.model.vo.base.PageVo;
import com.atguigu.daijia.model.vo.order.*;
import com.atguigu.daijia.order.mapper.OrderBillMapper;
import com.atguigu.daijia.order.mapper.OrderInfoMapper;
import com.atguigu.daijia.order.mapper.OrderProfitsharingMapper;
import com.atguigu.daijia.order.mapper.OrderStatusLogMapper;
import com.atguigu.daijia.order.service.OrderInfoService;
import com.atguigu.daijia.order.service.OrderMonitorService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {


    @Resource
    private OrderInfoMapper orderInfoMapper;
    @Resource
    private OrderStatusLogMapper orderStatusLogMapper;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private OrderMonitorService orderMonitorService;
    @Resource
    private OrderBillMapper orderBillMapper;
    @Resource
    private OrderProfitsharingMapper orderProfitsharingMapper;
    @Override
    public Long saveOrderInfo(OrderInfoForm orderInfoForm) {
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderInfoForm,orderInfo);
        String orderNo = UUID.randomUUID().toString().replaceAll("-", "");
        orderInfo.setOrderNo(orderNo);
        orderInfo.setStatus(OrderStatus.WAITING_ACCEPT.getStatus());
        orderInfoMapper.insert(orderInfo);

        log(orderInfo.getId(), orderInfo.getStatus());
        //TODO 这里redis的KEY可能有问题
        redisTemplate.opsForValue().set(RedisConstant.ORDER_ACCEPT_MARK,"0",
                RedisConstant.ORDER_ACCEPT_MARK_EXPIRES_TIME, TimeUnit.MINUTES);

        return orderInfo.getId();
    }

    @Override
    public Integer getOrderStatus(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().
                eq(OrderInfo::getId, orderId).select(OrderInfo::getStatus));
        if(orderInfo == null){
            return OrderStatus.NULL_ORDER.getStatus();
        }
        return orderInfo.getStatus();
    }

    public void log(Long orderId, Integer status){
        OrderStatusLog orderStatusLog = new OrderStatusLog();
        orderStatusLog.setOrderId(orderId);
        orderStatusLog.setOrderStatus(status);
        orderStatusLog.setOperateTime(new Date());
        orderStatusLogMapper.insert(orderStatusLog);
    }

    /**
     * 抢单新订单
     * 该方法用于处理司机抢单的逻辑，通过Redis实现分布式锁来确保订单的并发处理是线程安全的
     * 只有在订单未被接受的情况下，司机才能成功抢到订单，并更新订单状态
     *
     * @param driverId 司机的ID
     * @param orderId 订单的ID
     * @return 操作是否成功布尔值，这里固定返回true，实际业务中可能需要根据操作结果返回
     */
    @Override
    public Boolean robNewOrder(Long driverId, Long orderId) {
        // 检查订单是否已被接受的标记是否存在，如果存在则不允许抢订单
        if(Boolean.FALSE.equals(redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK))) {
            throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
        }
        // 使用Redisson客户端创建一个分布式锁，以确保并发时订单只能被一个司机接受
        RLock lock = redissonClient.getLock(RedisConstant.ROB_NEW_ORDER_LOCK + orderId);
        try {
            // 尝试获取锁，如果在指定时间内未获取到锁，则返回false
            boolean flag = lock.tryLock(RedisConstant.ROB_NEW_ORDER_LOCK_WAIT_TIME,
                    RedisConstant.ROB_NEW_ORDER_LOCK_LEASE_TIME, TimeUnit.SECONDS);
            if(flag) {
                // 再次检查订单是否已被接受，确保在获取锁之后订单状态未改变
                if(Boolean.FALSE.equals(redisTemplate.hasKey(RedisConstant.ORDER_ACCEPT_MARK))) {
                    throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
                }
                // 准备更新订单的信息
                OrderInfo orderInfo = new OrderInfo();
                orderInfo.setId(orderId);
                orderInfo.setStatus(OrderStatus.ACCEPTED.getStatus());
                orderInfo.setAcceptTime(new Date());
                orderInfo.setDriverId(driverId);
                // 更新数据库中的订单状态
                int row = orderInfoMapper.updateById(orderInfo);
                // 如果更新失败，则抛出异常
                if (row != 1) {
                    throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
                }
                // 记录日志，记录订单状态的变化
                this.log(orderId, orderInfo.getStatus());

                // 删除Redis中表示订单未被接受的标记，表示订单已经被成功接受
                redisTemplate.delete(RedisConstant.ORDER_ACCEPT_MARK);
            }
        }catch (Exception e){
            // 捕获异常，表示抢单失败，抛出自定义异常
            throw new GuiguException(ResultCodeEnum.COB_NEW_ORDER_FAIL);
        }finally {
            // 释放锁，确保即使在异常情况下也能释放资源
            if(lock.isLocked()){
                lock.unlock();
            }
        }
        // 返回成功标识
        return true;
    }

    @Override
    public CurrentOrderInfoVo searchCustomerCurrentOrder(Long customerId) {
        Integer[] statusArray = {
                OrderStatus.ACCEPTED.getStatus(),
                OrderStatus.DRIVER_ARRIVED.getStatus(),
                OrderStatus.UPDATE_CART_INFO.getStatus(),
                OrderStatus.START_SERVICE.getStatus(),
                OrderStatus.END_SERVICE.getStatus(),
                OrderStatus.UNPAID.getStatus()
        };
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().
                eq(OrderInfo::getCustomerId, customerId).
                in(OrderInfo::getStatus, statusArray).
                orderByDesc(OrderInfo::getId).
                last("limit 1"));
        CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo();
        if(orderInfo != null){
            currentOrderInfoVo.setOrderId(orderInfo.getId());
            currentOrderInfoVo.setStatus(orderInfo.getStatus());
            currentOrderInfoVo.setIsHasCurrentOrder(true);
        }
        currentOrderInfoVo.setIsHasCurrentOrder(false);
        return currentOrderInfoVo;
    }

    @Override
    public CurrentOrderInfoVo searchDriverCurrentOrder(Long driverId) {
        Integer[] statusArray = {
                OrderStatus.ACCEPTED.getStatus(),
                OrderStatus.DRIVER_ARRIVED.getStatus(),
                OrderStatus.UPDATE_CART_INFO.getStatus(),
                OrderStatus.START_SERVICE.getStatus(),
                OrderStatus.END_SERVICE.getStatus(),
                OrderStatus.UNPAID.getStatus()
        };
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().
                eq(OrderInfo::getDriverId, driverId).
                in(OrderInfo::getStatus, statusArray).
                orderByDesc(OrderInfo::getId).
                last("limit 1"));
        CurrentOrderInfoVo currentOrderInfoVo = new CurrentOrderInfoVo();
        if(orderInfo != null){
            currentOrderInfoVo.setOrderId(orderInfo.getId());
            currentOrderInfoVo.setStatus(orderInfo.getStatus());
            currentOrderInfoVo.setIsHasCurrentOrder(true);
        }
        currentOrderInfoVo.setIsHasCurrentOrder(false);
        return currentOrderInfoVo;
    }

    @Override
    public Boolean driverArriveStartLocation(Long orderId, Long driverId) {
        int update = orderInfoMapper.update(null, new LambdaUpdateWrapper<OrderInfo>().eq(OrderInfo::getId, orderId).
                eq(OrderInfo::getDriverId, driverId).set(OrderInfo::getStatus, OrderStatus.DRIVER_ARRIVED.getStatus()).
                set(OrderInfo::getArriveTime, new Date()));
        if(update == 1){
            this.log(orderId, OrderStatus.DRIVER_ARRIVED.getStatus());
        }else{
            throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
        }

        return true;
    }

    @Override
    public Boolean updateOrderCart(UpdateOrderCartForm updateOrderCartForm) {
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(updateOrderCartForm, orderInfo);
        orderInfo.setStatus(OrderStatus.UPDATE_CART_INFO.getStatus());
        int update = orderInfoMapper.update(orderInfo, new LambdaUpdateWrapper<OrderInfo>().
                eq(OrderInfo::getId, updateOrderCartForm.getOrderId()).
                eq(OrderInfo::getDriverId, updateOrderCartForm.getDriverId()));
        if(update == 1){
            this.log(updateOrderCartForm.getOrderId(), OrderStatus.UPDATE_CART_INFO.getStatus());
        }else{
            throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
        }
        return true;
    }

    @Override
    public Boolean startDrive(StartDriveForm startDriveForm) {
        int update = orderInfoMapper.update(null, new LambdaUpdateWrapper<OrderInfo>().
                eq(OrderInfo::getId, startDriveForm.getOrderId()).
                eq(OrderInfo::getDriverId, startDriveForm.getDriverId()).
                set(OrderInfo::getStatus, OrderStatus.START_SERVICE.getStatus()));
        if(update == 1){
            this.log(startDriveForm.getOrderId(), OrderStatus.START_SERVICE.getStatus());
        }else {
            throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
        }

        OrderMonitor orderMonitor = new OrderMonitor();
        orderMonitor.setOrderId(startDriveForm.getOrderId());
        orderMonitorService.saveOrderMonitor(orderMonitor);
        return true;
    }

    @Override
    public Long getOrderNumByTime(String startTime, String endTime) {
        return orderInfoMapper.selectCount(new LambdaQueryWrapper<OrderInfo>().
                ge(OrderInfo::getCreateTime, startTime).lt(OrderInfo::getCreateTime, endTime));
    }

    @Override
    public Boolean endDrive(UpdateOrderBillForm updateOrderBillForm) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setStatus(OrderStatus.END_SERVICE.getStatus());
        orderInfo.setRealAmount(updateOrderBillForm.getTotalAmount());
        orderInfo.setRealDistance(updateOrderBillForm.getRealDistance());
        orderInfo.setFavourFee(updateOrderBillForm.getFavourFee());
        orderInfo.setEndServiceTime(new Date());
        int update = orderInfoMapper.update(orderInfo, new LambdaUpdateWrapper<OrderInfo>().
                eq(OrderInfo::getId, updateOrderBillForm.getOrderId()).
                eq(OrderInfo::getDriverId, updateOrderBillForm.getDriverId()));
        if(update == 1){
            //记录日志
            this.log(updateOrderBillForm.getOrderId(), OrderStatus.END_SERVICE.getStatus());
            //插入实际账单数据
            OrderBill orderBill = new OrderBill();
            BeanUtils.copyProperties(updateOrderBillForm, orderBill);
            orderBill.setOrderId(updateOrderBillForm.getOrderId());
            orderBill.setPayAmount(updateOrderBillForm.getTotalAmount());
            orderBillMapper.insert(orderBill);

            //插入分账信息数据
            OrderProfitsharing orderProfitsharing = new OrderProfitsharing();
            BeanUtils.copyProperties(updateOrderBillForm, orderProfitsharing);
            orderProfitsharing.setOrderId(updateOrderBillForm.getOrderId());
            orderProfitsharing.setStatus(1);
        }else{
            throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
        }
        return true;
    }

    @Override
    public PageVo findCustomerOrderPage(Page<OrderInfo> pageParam, Long customerId) {
        IPage<OrderListVo> orderListVoIPage = orderInfoMapper.selectCustomerOrderPage(pageParam, customerId);
        return new PageVo(orderListVoIPage.getRecords(), orderListVoIPage.getPages(), orderListVoIPage.getTotal());
    }

    @Override
    public PageVo findDriverOrderPage(Page<OrderInfo> pageParam, Long driverId) {
        IPage<OrderListVo> orderListVoIPage = orderInfoMapper.selectDriverOrderPage(pageParam, driverId);
        return new PageVo(orderListVoIPage.getRecords(), orderListVoIPage.getPages(), orderListVoIPage.getTotal());
    }

    @Override
    public OrderBillVo getOrderBillInfo(Long orderId) {
        OrderBill orderBill = orderBillMapper.selectOne(new LambdaQueryWrapper<OrderBill>().eq(OrderBill::getOrderId, orderId));
        OrderBillVo orderBillVo = new OrderBillVo();
        BeanUtils.copyProperties(orderBill, orderBillVo);
        return orderBillVo;
    }

    @Override
    public OrderProfitsharingVo getOrderProfitsharing(Long orderId) {
        OrderProfitsharing orderProfitsharing = orderProfitsharingMapper.
                selectOne(new LambdaQueryWrapper<OrderProfitsharing>().
                eq(OrderProfitsharing::getOrderId, orderId));
        OrderProfitsharingVo orderProfitsharingVo = new OrderProfitsharingVo();
        BeanUtils.copyProperties(orderProfitsharing, orderProfitsharingVo);
        return orderProfitsharingVo;
    }

    /**
     * 发送订单账单信息
     * 该方法通过更新订单信息表中的订单状态为“未支付”，来记录一次订单的发送动作
     */
    @Override
    public Boolean sendOrderBillInfo(Long orderId, Long driverId) {
        // 更新订单状态为未支付
        int update = orderInfoMapper.update(null, new LambdaUpdateWrapper<OrderInfo>().
                eq(OrderInfo::getId, orderId).
                eq(OrderInfo::getDriverId, driverId).
                set(OrderInfo::getStatus, OrderStatus.UNPAID.getStatus()));

        // 如果更新成功，记录日志
        if(update == 1){
            this.log(orderId, OrderStatus.UNPAID.getStatus());
        } else {
            // 如果更新失败，抛出异常
            throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
        }

        // 返回成功标识
        return true;
    }

    @Override
    public OrderPayVo getOrderPayVo(String orderNo, Long customerId) {
        OrderPayVo orderPayVo = orderInfoMapper.selectOrderPayVo(orderNo, customerId);
        if(null != orderPayVo) {
            String content = orderPayVo.getStartLocation() + " 到 " + orderPayVo.getEndLocation();
            orderPayVo.setContent(content);
        }
        return orderPayVo;
    }
}
